
#define GL_GLEXT_PROTOTYPES

#include "openglframe.h"
#include <algorithm>

/// Performs an OpenGL error checking
void checkingGLerrors(const char *label) {
   
   GLenum errCode;
   const GLubyte *errStr;
   if ((errCode = glGetError()) != GL_NO_ERROR) {
      errStr = gluErrorString(errCode);
      printf("OpenGL ERROR: ");
      printf((char*)errStr);
      printf("(Label: ");
      printf(label);
      printf(")\n.");
   }
}

//~ /// Returns a normalized (unit) vector.
//~ /// @param v input vector.
//~ /// @return v normalized. 
//~ inline Vector normalize (const Vector& v) {
    //~ double len = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    //~ return Vector(v[0]/len,v[1]/len,v[2]/len);
//~ }

//~ /// Returns the rotation part of a transformation
//~ /// @param t Input transformation
//~ /// @return Rotation transformation
//~ inline Transformation rotationComponent (const Transformation& t) 
//~ {
    //~ Vector v0 = normalize(Vector (t.m(0,0), t.m(1,0), t.m(2,0)));
    //~ Vector v1 = normalize(Vector (t.m(0,1), t.m(1,1), t.m(2,1)));
    //~ Vector v2 = normalize(Vector (t.m(0,2), t.m(1,2), t.m(2,2)));
    //~ return Transformation (v0.x(), v1.x(), v2.x(),
                           //~ v0.y(), v1.y(), v2.y(),
                           //~ v0.z(), v1.z(), v2.z());
//~ }

OpenGLFrame::OpenGLFrame (QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags f)
   : QGLWidget (parent, shareWidget, f), arcball(1,1) {
   
   // for keyboard use 
   setFocusPolicy(Qt::StrongFocus);
   
   sceneTransform = Transformation(CGAL::IDENTITY);
   cursor3D = Point(0,0,0);
   interactionMode = VIEW_MODE;
   
   model = 0;
   deformer = 0;
   
   fovy = 40.0;
   zoomFactor = 1.0;
   meshrendermode_mask = 0x20;
   
   isProjOrtho = true; // orthographic projection
   is_lightingOn = true;
   on_deformation = false;
   
   texConstraints = 0;
   texFloor = 0;
   
   viewitems_mask = 0x0; 
   viewitems_mask = viewitems_mask | MODELDEF;
   viewitems_mask = viewitems_mask | SKELETONDEF;
   viewitems_mask = viewitems_mask | KEYPOINTS;
   viewitems_mask = viewitems_mask | CONSTRAINTS;
   //~ viewitems_mask = viewitems_mask | FLOOR;
   viewitems_mask = viewitems_mask | INFO;
   
   Vector displacement = cursor3D - Point(0,0,0);
}

OpenGLFrame::~OpenGLFrame() {
   
   if (model!=0)        delete model;
}

/// Sets the interaction mode
void OpenGLFrame::setInteractionMode (InteractionMode m) 
{
   interactionMode = m;
}

bool OpenGLFrame::loadShape(const QString& filename) {
   
   QFileInfo fi(filename);
   QString extension = fi.suffix();
   Mesh *newmodel = 0;
   if (extension == "off")
      newmodel = Mesh::load_off(filename.toStdString().c_str());
   else if (extension == "ply")
      newmodel = Mesh::load_ply(filename.toStdString().c_str());
   else 
      return false;
   
   if (newmodel) {
      if (model != 0) delete model;
      model = newmodel;
      genWorldLimits();
      setGLview();
      
      sceneTransform = Transformation(CGAL::IDENTITY); /// reset the transformation matrix
      
      return true;
   }
   return false;
}

/// OpenGL initialization.
void OpenGLFrame::initializeGL() {
   
   glClearColor(1,1,1,0);
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_LIGHT1);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   glShadeModel(GL_SMOOTH);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   
   // lighting setup
   GLfloat left_light_position[] =  {0.0, 2.0, 2.0, 0.0};
   GLfloat right_light_position[] = {0.0, -2.0, 2.0, 0.0};
   glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
   glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
   GLfloat left_diffuse_light[] =  {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat right_diffuse_light[] = {1.0f, 1.0f, 1.0f, 1.0f};
   glLightfv(GL_LIGHT0, GL_DIFFUSE, left_diffuse_light);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, right_diffuse_light);
   
   genWorldLimits();
   
   //~ glActiveTexture(GL_TEXTURE0);
   texConstraints = new Texture("icons/orangeT.png", GL_TEXTURE_2D);
   assert(texConstraints != 0);
   
   texFloor = new Texture("icons/floor.png", GL_TEXTURE_2D);
   assert(texFloor != 0);
   
   //~ setGLview();
}

/// Performs a glMultMatrix where the matrix is a CGAL transformation.
/// @param t CGAL tranformation matrix.
void glTransform (const Transformation &t)
{
    double matrix [] = { t.m(0,0), t.m(1,0), t.m(2,0), 0,
                         t.m(0,1), t.m(1,1), t.m(2,1), 0,
                         t.m(0,2), t.m(1,2), t.m(2,2), 0,
                         t.m(0,3), t.m(1,3), t.m(2,3), 1 };
    glMultMatrixd (matrix);
}

/// A simple opengl drawing callback.
void OpenGLFrame::paintGL() {
   
   setGLview();
   
   glClearStencil(0x1);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(cameye[0],cameye[1],cameye[2],camcenter[0],camcenter[1],camcenter[2],0,1,0);
   
   if (model == 0) return;
   
   // Draw the 3D cursor
   const Number &scalefactor = (maxcoo[0]-mincoo[0])*(1.5/100);
   //~ drawCursor3D(cursor3D, scalefactor);
   
   glTransform(sceneTransform);
   
   // Draw the floor
   if ((viewitems_mask & FLOOR) == FLOOR)
      drawFloor();
   
   // Draw the model
   if ((viewitems_mask & MODELDEF) == MODELDEF) {
      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_NORMAL_ARRAY);
      //~ glEnableClientState(GL_COLOR_ARRAY);
      //~ glVertexPointer(3, GL_FLOAT, 0, &model->vpositions[0]);
      //~ glColorPointer(3, GL_FLOAT, 0, &model->colors[0]);
      //~ glNormalPointer(GL_FLOAT, 0, &model->normals[0]);
      
      typedef Mesh::Vertex Vertex;
      Vertex_iterator vi = model->vertices_begin();
      glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &(vi->point()[0]));
      glNormalPointer(GL_FLOAT, sizeof(Vertex), &(vi->normal()[0]));
      
      static GLfloat colrf[4] = {0.0f, 0.5f, 1.0f, 1.0f };
      static GLfloat colrb[4] = {1.0f, 0.0f, 0.0f, 1.0f };
      glMaterialfv(GL_FRONT, GL_DIFFUSE, colrf);
      glMaterialfv(GL_BACK, GL_DIFFUSE, colrb);
      
      glLineWidth(1.0);
      (is_lightingOn)? glEnable(GL_LIGHTING):glDisable(GL_LIGHTING); 
      glDrawModel(model, meshrendermode_mask);
   }
   
   { // draw the constraints using GL_POINT_SPRITE
   glDisable(GL_LIGHTING);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glEnable(GL_TEXTURE_2D);
   glTexEnvf(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
   texConstraints->bind();
   
   glColor4f(1.0f, 1.0f, 1.0f, 1.0);
   glPointSize(20.0);
   glEnable(GL_POINT_SPRITE);
   glDrawPositions(constraints);
   glDisable(GL_POINT_SPRITE);
   
   glDisable(GL_TEXTURE_2D);
   glDisable(GL_BLEND);
   glEnable(GL_LIGHTING);
   }
   
   // Render Shadow
   if ((viewitems_mask & FLOOR) == FLOOR) {
      glDisable(GL_LIGHTING);
      glColor4f(0.0f, 0.0f, 0.0f, 0.8);
      glPushMatrix();
      Point ls = rotationComponent(sceneTransform).inverse().transform(Point(5.0,12.0,12.0));
      float l[] = {ls[0], ls[1], ls[2]}; // Coordinates of the light source
      //~ float l[] = {5.0, 12.0, 12.0}; // Coordinates of the light source
      float n[] = {0.0, -1.0, 0.0}; // Normal vector for the plane
      float e[] = {0.0, -1.0, 0.0}; // Point of the plane
      glShadowProjection(l,n,e);
      glDrawModel(model, FLAT);
      glPopMatrix();
      glEnable(GL_LIGHTING);
   }
   
   // Render info
   if ((viewitems_mask & INFO) == INFO and model != 0) {
      glDisable(GL_LIGHTING);
      glDisable(GL_DEPTH_TEST);
      
         frame++;
         std::time(&end);
         double ddifftime = std::difftime(end, start);
         if (ddifftime > 1) {
            cfps = frame/ddifftime;
            start = end;
            frame = 0;
         }
      
      QString strInfoNv=QString("vertices: %1").arg(model->nvertices());
      QString strInfoNf=QString("faces: %1").arg(model->nfaces());
      QString strInfoFPS=QString("FPS: %1").arg(cfps,7,'f',1);
      
      glColor4f(0.0, 0.0, 0.0, 1.0);
      renderText(30,15, strInfoNv);
      renderText(30,30, strInfoNf);
      renderText(30,75, strInfoFPS);
      
      glEnable(GL_LIGHTING);
      glEnable(GL_DEPTH_TEST);
   }
}

void OpenGLFrame::drawFloor() {
   
   glEnable(GL_TEXTURE_2D);
   texFloor->bind();
   
   static GLfloat colrf[4] = {1.0f, 1.0f, 1.0f, 1.0f };
   glMaterialfv(GL_FRONT, GL_DIFFUSE, colrf);

   glPolygonMode(GL_FRONT, GL_FILL);
   glPolygonMode(GL_BACK, GL_LINE);
   //~ glDisable(GL_LIGHTING);
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1,1);
   glColor3f(1.0,1.1,1.0);
   glBegin(GL_QUADS);
   glNormal3f(0.0,1.0,0.0);
   glTexCoord2d(0.0,0.0); glVertex3f(-3.5,-1.0, 3.5);
   glTexCoord2d(1.0,0.0); glVertex3f( 3.5,-1.0, 3.5);
   glTexCoord2d(1.0,1.0); glVertex3f( 3.5,-1.0,-3.5);
   glTexCoord2d(0.0,1.0); glVertex3f(-3.5,-1.0,-3.5);
   glEnd();
   //~ glEnable(GL_LIGHTING);
   glDisable(GL_POLYGON_OFFSET_FILL);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   glColor4f(0.0f, 0.5f, 1.0f, 1.0f);
   
   glDisable(GL_TEXTURE_2D);
}

// This function is from http://www.devmaster.net community.
void OpenGLFrame::glShadowProjection(float *l, float *e, float *n) {
   
   float d, c;
   float mat[16];
   
   // These are c and d (corresponding to the tutorial)
   d = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
   c = e[0]*n[0] + e[1]*n[1] + e[2]*n[2] - d;
   
   // Create the matrix. OpenGL uses column by column ordering
   mat[0]  = l[0]*n[0]+c;
   mat[4]  = n[1]*l[0];
   mat[8]  = n[2]*l[0];
   mat[12] = -l[0]*c-l[0]*d;
  
   mat[1]  = n[0]*l[1];
   mat[5]  = l[1]*n[1]+c;
   mat[9]  = n[2]*l[1];
   mat[13] = -l[1]*c-l[1]*d;
  
   mat[2]  = n[0]*l[2];
   mat[6]  = n[1]*l[2];
   mat[10] = l[2]*n[2]+c;
   mat[14] = -l[2]*c-l[2]*d;
  
   mat[3]  = n[0];
   mat[7]  = n[1];
   mat[11] = n[2];
   mat[15] = -d;

   // Finally multiply the matrices together *plonk*
   glMultMatrixf(mat);
}

/// It computes the correct world limits (used in the openglWorld definition)
/// It's computed in function of current model's BB 
/// It avoids distorsion on visualization
void OpenGLFrame::genWorldLimits() {
   
   if (model != 0) {
      mincoo[0] = model->mincoo[0]; mincoo[1] = model->mincoo[1]; mincoo[2] = model->mincoo[2];
      maxcoo[0] = model->maxcoo[0]; maxcoo[1] = model->maxcoo[1]; maxcoo[2] = model->maxcoo[2];
   }
   else {
      mincoo[0] = -1; mincoo[1] = -1; mincoo[2] = -1;
      maxcoo[0] =  1; maxcoo[1] =  1; maxcoo[2] =  1;
   }
   
   float xc = mincoo[0] + 0.5*(maxcoo[0]-mincoo[0]);
   float yc = mincoo[1] + 0.5*(maxcoo[1]-mincoo[1]);
   
   float WorldWidth  = abs(maxcoo[0]-mincoo[0]);
   float WorldHeight = abs(maxcoo[1]-mincoo[1]);
   
   if ((WorldHeight/WorldWidth) > ((float)height()/(float)width())) {
      WorldWidth = WorldHeight / ((float)height()/(float)width());
   }
   else if ((WorldHeight/WorldWidth) < ((float)height()/(float)width())) {
      WorldHeight = WorldWidth * ((float)height()/(float)width());
   }
   
   // world limits
   mincoo[0] = xc - 0.5*WorldWidth;
   mincoo[1] = yc - 0.5*WorldHeight;
   
   maxcoo[0] = xc + 0.5*WorldWidth;
   maxcoo[1] = yc + 0.5*WorldHeight;
   
   // center camera
   camcenter[0] = xc;
   camcenter[1] = yc;
   camcenter[2] = mincoo[2] + 0.5*(maxcoo[2]-mincoo[2]);
}

/// It sets the projection/modelview matrices
void OpenGLFrame::setGLview() {
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   
   WorldWidth  = abs(maxcoo[0]-mincoo[0])*0.5;
   WorldHeight = abs(maxcoo[1]-mincoo[1])*0.5;
   
   objDistance = 0.8660*(maxcoo[1]-mincoo[1]); /// sqrt(3)/2 = 0.8660
   //~ objDistance = WorldHeight;
   nearplane = objDistance - objDistance*0.5;
   farplane  = nearplane + 20.0;
   
   cameye[0] = camcenter[0];
   cameye[1] = camcenter[1];
   cameye[2] = maxcoo[2] + objDistance;
   
   if (isProjOrtho) 
      glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
              -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
               nearplane, farplane);
   else 
      gluPerspective(fovy*zoomFactor, (GLfloat)width()/(GLfloat)height(), nearplane, farplane);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0, 0, width(), height());
}
   
/// A simple resize callback.
/// @param width the width of the new window.
/// @param height the height of the new window.
void OpenGLFrame::resizeGL(int width, int height) 
{
   checkingGLerrors("resizeGL()");
   setGLview();
   arcball.setBounds(width, height);  //Update mouse bounds for the arcball
}

/// Callback for handling mouse button press.
/// @param event encapsulates the event's info.
void OpenGLFrame::mousePressEvent(QMouseEvent *event) {
   
   std::list<uint> selected;
   // Do a different thing depending on interactionMode
   switch (interactionMode) {
      case SELECT_MODE: {
         break;
      }
      case VIEW_MODE: {
         if (event->button() == Qt::LeftButton) {
            if (deformer != 0) {
               grabStart = Point (event->x(), height() - event->y(), 0);
               grabLast = grabStart = screenToWorldOrtho(grabStart);
               
               selection.begin(event->x(),height()-event->y());
               glDrawForSelection(constraints);
               selection.end(selected);
               if (not selected.empty()) {
                  id_moving = selected.front();
                  on_deformation = true;
               }
               else 
                  on_deformation = false;
            }
            else {
               if (model == 0) break;
               selection.begin(event->x(),height()-event->y());
               GLdrawFacesForSelection(model);
               //~ glDrawVerticesForSelection(model);
               selection.end(selected);
               cout<<"selected.size(): "<<selected.size()<<endl;
               // Include the object being altered
               if (not selected.empty()) {
                  unsigned idf = selected.front();
                  const uint *ff = &model->faces[idf*3]; // front face 
                  Vertex_iterator vi = model->vertices_begin();
                  int idv = vi[ff[0]].halfedge()->vertex()->id();
                  const Point3 &p = vi[ff[0]].halfedge()->vertex()->point();
                  constraints.push_back(p,idv/3);
                  
                  //~ int idv = vi[ff[0]].halfedge()->vertex()->id();
                  //~ float *p = &model->vpositions[idv];
                  //~ constraints.push_back(Point3(p[0],p[1],p[2]),idv/3);
                  //~ constraints.push_back(MLS::Point3(p[0],p[1],p[2]));
                  
                  /*
                  int id = selected.front();
                  cout<<"id: "<<id<<" :: "<<selected.back()<<endl;
                  Face_iterator fit = model->faces_begin();
                  advance(fit,id);
                  const MLS::Point3 &p = model->getPosition(fit->halfedge()->vertex());
                  int idv = fit->halfedge()->vertex()->id();
                  //~ Vertex_iterator vit = model->vertices_begin();
                  //~ advance(vit,id);
                  //~ const Point &p = vit->point();
                  constraints.push_back(p,idv);
                  */
               }
            }
         }
         else if (event->button() == Qt::MidButton) {
            // Middle button rotates the object
            arcball.click(event->x(),height()-event->y());
            objectToScene = sceneTransform;
         }
         else if (event->button() == Qt::RightButton) {
            // Right button translates
            grabStart = Point (event->x(), height() - event->y(), 0);
            pixelDisplacement[0] = screenToWorld (grabStart + Vector (1,0,0)) - screenToWorld (grabStart);
            pixelDisplacement[1] = screenToWorld (grabStart + Vector (0,1,0)) - screenToWorld (grabStart);
            
            objectToScene = sceneTransform;
         }
         break; 
      }
   }
   
   glDraw();
}

/// Callback for handling mouse movements.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseMoveEvent(QMouseEvent *event) {
   
   switch (interactionMode) {
      case SELECT_MODE: {
         break;
      }
      case VIEW_MODE: {
         if ((event->buttons() & Qt::LeftButton) ) {
            if (on_deformation) {
               // Update the deformed positions of control points aand compute the deformation
               Point grabEnd(event->x(), height() - event->y(), 0);
               grabEnd = screenToWorldOrtho(grabEnd);
               Vector wt = grabEnd-grabLast;
               constraints.translate(id_moving, Vector3(wt[0],wt[1],wt[2]));
               deformer->execute_deformation(&constraints);
               
               grabLast = grabEnd;
               glDraw();
            }
         }
         else if ((event->buttons() & Qt::MidButton) ) {
            // Pass the mouse movement to the arcball.
            /// arcball->mouseDrag(event->x(),height()-event->y());
            arcball.drag(event->x(),height()-event->y(),tM);
            Transformation mTransf(tM[0], tM[4], tM[8],  tM[12],
                                   tM[1], tM[5], tM[9],  tM[13],
                                   tM[2], tM[6], tM[10], tM[14]);
            sceneTransform = mTransf*objectToScene;
            glDraw();
         }
         else if (event->buttons() & Qt::RightButton) {
            // Translation
            Point grabEnd(event->x(), height() - event->y(), 0);
            Vector displacement = (grabEnd-grabStart);
            Vector worldDisplacement = pixelDisplacement[0]*displacement[0] + pixelDisplacement[1]*displacement[1];
            
            sceneTransform = objectToScene*Transformation(CGAL::TRANSLATION, worldDisplacement);
            glDraw();
         }
         break;
      }
      default: {}
   }
}

/// Callback for handling mouse button release.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseReleaseEvent(QMouseEvent *event)
{
   switch (interactionMode) {
      case SELECT_MODE: {
      
         break;
      }
      case VIEW_MODE: {
         on_deformation = false;
         break;
      }
   }
   event->accept(); 
}

/// Inplements zoom operations using the mouse wheel
void OpenGLFrame::wheelEvent(QWheelEvent *e) {
   
   //~ if (e->delta()>0) zoomFactor+= 0.1;
   //~ else              zoomFactor-= 0.1;
   //~ e->accept(); 
   //~ setGLview();
   //~ glDraw();
   
   if (e->delta()>0) zoomIn();
   else              zoomOut();
      
   e->accept(); 
}

void OpenGLFrame::keyPressEvent(QKeyEvent *event) {
   
   if (event->key() == Qt::Key_Escape) {
      exit(0);
   }
}

/// Implements the "zoomin" operation. Narrows the view of the scene.
void OpenGLFrame::zoomIn ()
{
   sceneTransform = Transformation (CGAL::SCALING, 1.2) * sceneTransform;
   glDraw();
}

/// Implements the "zoomout" operation. Enlarges the view of the scene.
void OpenGLFrame::zoomOut ()
{
   sceneTransform = Transformation (CGAL::SCALING, 1.0/1.2) * sceneTransform;
   glDraw();
}

/// Implements the "zoomAll" operation
/// Computes a scene viewing transformation so that all objects are shown.
void OpenGLFrame::zoomAll ()
{
   if (model == 0) {
      // A simple identity transformation suffices for exhibiting 0 objects
      sceneTransform = Transformation(CGAL::IDENTITY);
   } else {
      sceneTransform = Transformation(CGAL::IDENTITY);
      // For scenes with several objects
      // Number d = ?
      // Point center = ?
      //~ sceneTransform = Transformation(CGAL::SCALING, 2.0/d) 
                      //~ *Transformation(CGAL::TRANSLATION, center-Point(0,0,0))
                      //~ *rotationComponent(sceneTransform);
   }
   glDraw();
}

void OpenGLFrame::updateMesh() {
   
   // clear deformation data
   if (deformer != 0) {
      deformer->clear();
      delete deformer;
   }
   deformer = 0;
   
   computeDeformation();
}

void OpenGLFrame::clear() {
   
   // constraints data
   constraints.clear();
   
   // deformer data
   if (deformer != 0) {
      deformer->clear();
      delete deformer;
   }
   deformer = 0;
   
   // model data
   if (model != 0) delete model;
   model = 0;
   
   // glview data
   genWorldLimits();
   setGLview();
   sceneTransform = Transformation(CGAL::IDENTITY); /// reset the transformation matrix
}

Point OpenGLFrame::screenToWorld (Point p) {
   
   double wldcoord[3];
   glGetIntegerv (GL_VIEWPORT, viewport);
   glGetDoublev (GL_MODELVIEW_MATRIX, mMtx);
   
   /// Compute the world coordinates using the orthographic projection
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
           -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
            nearplane, farplane);
   glGetDoublev (GL_PROJECTION_MATRIX, pMtx);
   glPopMatrix();
   
   gluUnProject (p.x(), p.y(), p.z(),mMtx, pMtx, viewport,
                 &wldcoord[0], &wldcoord[1], &wldcoord[2]);
   return Point (wldcoord[0], wldcoord[1], wldcoord[2]);
}

Point OpenGLFrame::screenToWorldOrtho (Point p) {
   
   double wldcoord[3];
   glGetIntegerv (GL_VIEWPORT, viewport);
   glGetDoublev (GL_MODELVIEW_MATRIX, mMtx);
   
   /// Compute the world coordinates using the orthographic projection
   glMatrixMode(GL_PROJECTION);
   glPushMatrix();
   glLoadIdentity();
   glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
           -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
            nearplane, farplane);
   glGetDoublev (GL_PROJECTION_MATRIX, pMtx);
   glPopMatrix();
   
   gluUnProject (p.x(), p.y(), p.z(),mMtx, pMtx, viewport,
                 &wldcoord[0], &wldcoord[1], &wldcoord[2]);
   return Point (wldcoord[0], wldcoord[1], wldcoord[2]);
}

void OpenGLFrame::computeDeformation() {
   
   if (deformer==0) {
      deformer = new DeformationMesh(model);
      deformer->setting(&constraints);
   }
}
